
#include "InputManager.h"
#include "GameServices.h"
#include "IOgreManager.h"
#include <OgreRenderWindow.h>

IInputManager* InputManager::_inputManager;

InputManager::InputManager() :
	_inputMgr(nullptr),_mouse(nullptr),_keyboard(nullptr),
	_listenerList(new std::vector<IInputListener*>()),
	_deadList(new std::list<IInputListener*>()),
	_newList(new std::list<IInputListener*>())
{
}

InputManager::~InputManager(){
	delete _listenerList;
	_listenerList = nullptr;
	delete _newList;
	_newList = nullptr;
	delete _deadList;
	_deadList = nullptr;
	_inputMgr->destroyInputObject(_mouse);
	_mouse = nullptr;
	_inputMgr->destroyInputObject(_keyboard);
	_keyboard = nullptr;
	_inputMgr->destroyInputSystem(_inputMgr);
	_inputMgr = nullptr;
	_inputManager = nullptr;
}
void InputManager::AddListener(IInputListener* newListener){
	for(std::list<IInputListener*>::iterator it = _newList->begin(); it != _newList->end(); ++it)
		if(newListener == (*it))
			return;

	_newList->push_back(newListener);
}

void InputManager::RemoveListener(IInputListener* oldListener){
	for(std::list<IInputListener*>::iterator it = _deadList->begin(); it != _deadList->end(); ++it)
		if(oldListener == (*it))
			return;		

	_deadList->push_back(oldListener);
}

bool InputManager::IsKeyDown(const OIS::KeyCode &key){
	return _keyboard->isKeyDown(key);
}

bool InputManager::IsKeyUp(const OIS::KeyCode &key){
	return !_keyboard->isKeyDown(key);
}

bool InputManager::IsMouseDown(const OIS::MouseButtonID &mouse){
	return (_mouse->getMouseState().buttons  &mouse) != 0;
}

bool InputManager::IsMouseUp(const OIS::MouseButtonID &mouse){
	return !IsMouseDown(mouse);
}

const OIS::MouseState &InputManager::GetMouseState(){
	return _mouse->getMouseState();
}

void InputManager::Initialize(){
	OIS::ParamList pl;
	size_t windowHnd = 0;
	std::ostringstream windowHndStr;
	
	GameServices::OgreManager()->GetRenderWindow()->getCustomAttribute("WINDOW", &windowHnd);
    windowHndStr << windowHnd;
    pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str()));

	_inputMgr = OIS::InputManager::createInputSystem(pl);
    _keyboard = static_cast<OIS::Keyboard*>(_inputMgr->createInputObject( OIS::OISKeyboard,true));
    _mouse = static_cast<OIS::Mouse*>(_inputMgr->createInputObject( OIS::OISMouse,true));
    _mouse->setEventCallback(this);
    _keyboard->setEventCallback(this);

}

void InputManager::UpdateStarted(const Ogre::FrameEvent &gameTime){
	while(_deadList->begin() != _deadList->end())
		for(_listenerIt = _listenerList->begin(); _listenerIt != _listenerList->end(); )
			if(_deadList->front() == (*_listenerIt)){
				_listenerIt = _listenerList->erase(_listenerIt);
				_deadList->pop_front();
			}else ++_listenerIt;
	if(_keyboard){
		_keyboard->capture();
		for(_keyIt = _keysDown.begin(); _keyIt != _keysDown.end(); ++_keyIt)
			NotifyKeyHeld((*_keyIt));
	}
	while(_deadList->begin() != _deadList->end())
		for(_listenerIt = _listenerList->begin(); _listenerIt != _listenerList->end(); )
			if(_deadList->front() == (*_listenerIt)){
				_listenerIt = _listenerList->erase(_listenerIt);
				_deadList->pop_front();
			}else ++_listenerIt;
	if(_mouse){
		_mouse->capture();
		for(_mouseIt = _mouseDown.begin(); _mouseIt!=_mouseDown.end(); ++_mouseIt)
			NotifyMouseHeld(_mouse->getMouseState(), (*_mouseIt));
	}
}

void InputManager::UpdateEnded(const Ogre::FrameEvent &gameTime){
	while(_deadList->begin() != _deadList->end())
		for(_listenerIt = _listenerList->begin(); _listenerIt != _listenerList->end(); )
			if(_deadList->front() == (*_listenerIt)){
				_listenerIt = _listenerList->erase(_listenerIt);
				_deadList->pop_front();
			}else ++_listenerIt;
	while(_tempKeysDown.begin() != _tempKeysDown.end()){
		_keysDown.push_back(*_tempKeysDown.begin());
		_tempKeysDown.pop_front();
	}
	while(_deadList->begin() != _deadList->end())
		for(_listenerIt = _listenerList->begin(); _listenerIt != _listenerList->end(); )
			if(_deadList->front() == (*_listenerIt)){
				_listenerIt = _listenerList->erase(_listenerIt);
				_deadList->pop_front();
			}else ++_listenerIt;
	
	while(_tempMouseDown.begin() != _tempMouseDown.end()){
		_mouseDown.push_back(*_tempMouseDown.begin());
		_tempMouseDown.pop_front();
	}
	
	_tempMouseDown.clear();
	_tempKeysDown.clear();

	while(_deadList->begin() != _deadList->end())
		for(_listenerIt = _listenerList->begin(); _listenerIt != _listenerList->end(); )
			if(_deadList->front() == (*_listenerIt)){
				_listenerIt = _listenerList->erase(_listenerIt);
				_deadList->pop_front();
			}else ++_listenerIt;

	while(_newList->begin() != _newList->end()){
		_listenerList->push_back(_newList->front());
		_newList->pop_front();
	}

	_deadList->clear();
	_newList->clear();
}

void InputManager::Cleanup(){
	if(!_inputMgr)
		return;
	_mouse->setEventCallback(nullptr);
	_keyboard->setEventCallback(nullptr);
	_keysDown.clear();
	_tempKeysDown.clear();
	_mouseDown.clear();
	_tempMouseDown.clear();
	_listenerList->clear();
	_newList->clear();
	_deadList->clear();
}

bool InputManager::keyPressed(const OIS::KeyEvent &key){
	NotifyKeyDown(key.key);
	_tempKeysDown.push_back(key.key);
	return true;
}

bool InputManager::keyReleased(const OIS::KeyEvent &key){
	NotifyKeyUp(key.key);
	
	for(_keyIt = _keysDown.begin(); _keyIt != _keysDown.end(); )
		if((*_keyIt) == key.key){	
			_keyIt = _keysDown.erase(_keyIt);
			return true;
		}else ++_keyIt;
	return true;
}

bool InputManager::mouseMoved(const OIS::MouseEvent &mouse){
	NotifyMouseMoved(mouse.state);
	return true;
}

bool InputManager::mousePressed(const OIS::MouseEvent &mouse, OIS::MouseButtonID id){
	NotifyMouseDown(mouse.state, id);
	_tempMouseDown.push_back(id);
	return true;
}

bool InputManager::mouseReleased(const OIS::MouseEvent &mouse, OIS::MouseButtonID id){
	NotifyMouseUp(mouse.state, id);
	
	for(_mouseIt = _mouseDown.begin(); _mouseIt!=_mouseDown.end(); )
		if((*_mouseIt) == id){
			_mouseIt = _mouseDown.erase(_mouseIt);
			return true;
		}else ++_mouseIt;
	return true;
}

void InputManager::NotifyKeyDown(const OIS::KeyCode &key){
	for(_listenerIt = _listenerList->begin(); _listenerIt != _listenerList->end(); ++_listenerIt)
		(*_listenerIt)->OnKeyDown(key);
}

void InputManager::NotifyKeyUp(const OIS::KeyCode &key){
	for(_listenerIt = _listenerList->begin(); _listenerIt != _listenerList->end(); ++_listenerIt)
		(*_listenerIt)->OnKeyUp(key);
}

void InputManager::NotifyKeyHeld(const OIS::KeyCode &key){
	for(_listenerIt = _listenerList->begin(); _listenerIt != _listenerList->end(); ++_listenerIt)
		(*_listenerIt)->OnKeyHeld(key);
}

void InputManager::NotifyMouseDown(const OIS::MouseState &mouse, OIS::MouseButtonID id){
	for(_listenerIt = _listenerList->begin(); _listenerIt != _listenerList->end(); ++_listenerIt)
		(*_listenerIt)->OnMouseDown(mouse,id);
}

void InputManager::NotifyMouseUp(const OIS::MouseState &mouse, OIS::MouseButtonID id){
	for(_listenerIt = _listenerList->begin(); _listenerIt != _listenerList->end(); ++_listenerIt)
		(*_listenerIt)->OnMouseUp(mouse,id);
}

void InputManager::NotifyMouseHeld(const OIS::MouseState &mouse, OIS::MouseButtonID id){
	for(_listenerIt = _listenerList->begin(); _listenerIt != _listenerList->end(); ++_listenerIt)
		(*_listenerIt)->OnMouseHeld(mouse,id);
}

void InputManager::NotifyMouseMoved(const OIS::MouseState &mouse){
	for(_listenerIt = _listenerList->begin(); _listenerIt != _listenerList->end(); ++_listenerIt)
		(*_listenerIt)->OnMouseMoved(mouse);
}